#include "IO_Manager.h"



void MouseInfo::update(SDL_MouseMotionEvent& mouseMove){
	vec2i mouseCurrent(mouseMove.x, mouseMove.y);
	mouseRawRelative=mouseCurrent-mouseRawPosition;
	mouseRawPosition=mouseCurrent;
	mouseCartesianRelative.x=mouseRawRelative.x;
	mouseCartesianRelative.y=-mouseRawRelative.y;
	mouseCartesianPosition.x=mouseRawPosition.x;
	mouseCartesianPosition.y=screenSize.y-1-mouseRawPosition.y;
}
void MouseInfo::update(SDL_MouseButtonEvent& mouseClick){
	switch(mouseClick.button){
	case SDL_BUTTON_LEFT:
		left=mouseClick.state==SDL_PRESSED;
		break;
	case SDL_BUTTON_RIGHT:
		right=mouseClick.state==SDL_PRESSED;
		break;
	case SDL_BUTTON_MIDDLE:
		middle=mouseClick.state==SDL_PRESSED;
		break;
	}
}

IO_Manager::IO_Manager(): screen(NULL), drawFps(true), keepRunning(true),
		startTicks(0), scheduledRefresh(0), lastScreenRefreshTicks(0),
		fpsCap(10), contextID(1), gameContext(NULL),
		fps(25) {}
IO_Manager::~IO_Manager(){
	SDL_Quit();
}
bool IO_Manager::initialize_SDL(){
	SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
	startTicks=SDL_GetTicks();
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	GL_Init();
	if(!screen)
		return false;
	return true;
}
bool IO_Manager::initializeFont(const std::string& filename, unsigned size){
	try{
		default_font.open(filename, size);
		return true;
	}
	catch(...){
		printf("ERROR INITIALIZING FONT\n");
		return false;
	}
}
int IO_Manager::startMainLoop(Uint32 delay){
	GL_Init();
	int timeLeft;
	Uint32 now;
	Uint32 gameStart;
	Uint32 gameTime;
	while(keepRunning){
		//while we have time, handle events and simulate the game
		do{
			now = SDL_GetTicks();
			handleEvents();
			gameContext->processMouse(mouseinfo);
		}while(now < scheduledRefresh);
		gameContext->updateState();
		screenRefreshFunc();
		scheduledRefresh = now + delay;
	}
	return 0;
}
void IO_Manager::handleEvents(){
	while(SDL_PollEvent(&event)){
		switch(event.type){
		case SDL_QUIT:
			keepRunning = false;
			break;
		case SDL_VIDEORESIZE:
			GL_Init(event.resize.w, event.resize.h);
			if(gameContext)
				gameContext->resize(screenSize);
			break;
		case SDL_MOUSEMOTION:
			mouseinfo.update(event.motion);
			break;
		case SDL_MOUSEBUTTONDOWN:
		case SDL_MOUSEBUTTONUP:
			mouseinfo.update(event.button);
			break;
		}
	}
}
size_t IO_Manager::registerContext(Context& context){
	contexts[contextID]=&context;
	return contextID++;
}
void IO_Manager::registerGameContext(Context& context){
	gameContext=&context;
	if(!gameContext)
		return;
	//by default, the game context occupies entire screen
	gameContext->move(vec2i(0,0));
	gameContext->resize(screenSize);
	/* resize(vec2i) lets the game know how big it is
	 * This function will be called whenever the screen is resized.
	 * The GameContext class (in PowderGame.h) will override
	 * lots of these functions to also update it's parent GameManager
	 * class infos. Right now, it's just another layer. But it establishes a
	 * simple interface, so future changes to the game and it's particular
	 * context don't affect the IO_Manager
	 */
}
void IO_Manager::deregisterContext(size_t cID){
	contexts.erase(cID);
}
void IO_Manager::deregisterGameContext(){
	gameContext=NULL;
}
void IO_Manager::screenCoordinates(){
	glLoadIdentity();
}
void IO_Manager::cartesianCoordinates(){
	glLoadIdentity();
	glTranslatef(0.0f,float(screenSize.y-1),0.0f);
	glScalef(1.0f,-1.0f,1.0f);
}

void IO_Manager::GL_Init(int width, int height){
	screen = SDL_SetVideoMode(width,height,32,SDL_OPENGL|SDL_RESIZABLE);

	//glEnable(GL_TEXTURE_2D);

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	glViewport(0,0,width,height);

	glClear(GL_COLOR_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glOrtho(0, width, height, 0, -1, 1); //0,0 is upper left

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);

	screenSize.x=width;
	screenSize.y=height;
	mouseinfo.screenSize=screenSize;
}
void IO_Manager::screenRefreshFunc(){
	if(!gameContext){
		glClear(GL_COLOR_BUFFER_BIT);
		//draw lines from corners to mouse
		glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
		glBegin(GL_LINES);
		glVertex2i(0,0);
		glVertex2i(mouseinfo.mouseRawPosition.x, mouseinfo.mouseRawPosition.y);
		glVertex2i(screenSize.x-1,0);
		glVertex2i(mouseinfo.mouseRawPosition.x, mouseinfo.mouseRawPosition.y);
		glVertex2i(screenSize.x-1,screenSize.y-1);
		glVertex2i(mouseinfo.mouseRawPosition.x, mouseinfo.mouseRawPosition.y);
		glVertex2i(0,screenSize.y-1);
		glVertex2i(mouseinfo.mouseRawPosition.x, mouseinfo.mouseRawPosition.y);
		glEnd();
		if(drawFps){
			doDrawFps();
		}
		SDL_GL_SwapBuffers();
	}
	else{
		glClear(GL_COLOR_BUFFER_BIT);
		std::stringstream out;
		gameContext->renderToScreen(out);
		if(drawFps){
			doDrawFps();
		}
		glEnable(GL_TEXTURE_2D);
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
		default_font.drawText(16,default_font.getHeight(), out.str());
		glDisable(GL_TEXTURE_2D);
		SDL_GL_SwapBuffers();
	}
	lastScreenRefreshTicks=SDL_GetTicks();
}
void IO_Manager::doDrawFps(){
	glEnable(GL_TEXTURE_2D);
	glColor3f(1.0f, 1.0f, 1.0f);
	Uint32 mspf = SDL_GetTicks() - lastScreenRefreshTicks;
	fps.nextValue(1000.0f/mspf);
	default_font.drawText(16, default_font.getHeight()*2, "Frames Per Second: %.2f",fps.average());
	glDisable(GL_TEXTURE_2D);
}

Uint32 ScreenTimerFunc(Uint32 rate, void* param){
	SDL_Event event;
	event.type=SDL_USEREVENT;
	event.user.code=USER_SCREEN_REFRESH_REQUEST;
	event.user.data1=event.user.data2=0;
	SDL_PushEvent(& event);
	return rate;
}
Uint32 GameTimerFunc(Uint32 rate, void* param){
	SDL_Event event;
	event.type=SDL_USEREVENT;
	event.user.code=USER_GAME_REFRESH_REQUEST;
	event.user.data1=event.user.data2=0;
	SDL_PushEvent(& event);
	return rate;
}


Context::Context(){} //nothing by default
Context::Context(const vec2i& p, const vec2i& s) : position(p), size(s) {}
Context::~Context() {} //nothing
void Context::renderToScreen(){
	//make a stranslucent white box where this context is
	glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
	glBegin(GL_QUADS);
	glVertex2i(position.x, position.y);
	glVertex2i(position.x+size.x, position.y);
	glVertex2i(position.x+size.x, position.y+size.y);
	glVertex2i(position.x, position.y+size.y);
	glEnd();
}
void Context::renderToScreen(std::stringstream& out){
	renderToScreen();
}
void Context::updateState(){}
void Context::move(const vec2i& p){
	position=p;
}
void Context::moveRel(const vec2i& p){
	position+=p;
}
void Context::resize(const vec2i& s){
	size=s;
}
bool Context::isOccupied(const vec2i& point){
	vec2i local = point-position;
	return local.x>=0 && local.y>=0 && local.x<size.x && local.y<size.y;
}
bool Context::isOccupied(const vec2i& upperLeft, const vec2i& lowerRight){
	bool notIntersect = upperLeft.x > position.x+size.x
		|| upperLeft.y > position.y+size.y
		|| lowerRight.x < position.x
		|| lowerRight.y < position.y;
	return !notIntersect;
}
void Context::processKey(SDL_KeyboardEvent& kbe){} //does nothing
void Context::processMouse(SDL_MouseButtonEvent& mouseButton, const MouseInfo& mouseinfo){} //does nothing
void Context::processMouse(const MouseInfo& mouseinfo) {}





